<?php


namespace App\Common\UnitTool;
	/*
		HttpRequest Class
		function __construct2($Url,$Method)
		function __construct3($Url,$Method,$PostData)

		$hr = new HttpRequest("ssl://www.baidu.com:443/", "get");
		$hr = new HttpRequest("http://www.baidu.com:80/", "get");
		$hr = new HttpRequest("http://www.baidu.com:80/", "post", $PostData);
		$hr->referer = "";
		$hr->cookie = "";
		$hr->timeOut = 30;
		$hr->cacheSize = 2048;
		$RESULT = $hr.action();

		$RESULT 格式
		Array (
			头部信息
			[HTTP/1.1] => 200 OK
			[Content-Type] => text/plain
			[Last-Modified] => Thu, 15 Jun 2017 04:34:27 GMT
			[Accept-Ranges] => bytes
			[ETag] => "cd8851ac90e5d21:0"
			[Server] => Microsoft-IIS/7.5
			[X-Powered-By] => ASP.NET
			[Date] => Thu, 15 Jun 2017 06:49:41 GMT
			[Connection] => close
			[Content-Length] => 951
			正文信息
			[Document_Body] => ﻿
			错误信息
			[error] =>
		)

	*/
	class HttpRequest
    {
        /// 主机地址
        private $host = "";
        /// 协议传输方式 GET POST
        private $method = "get";
        /// 端口
        private $port = 0;
        /// 请求路径
        private $path = "";
        /// 请求参数
        private $query = "";
        /// POST方式要传输的数据
        private $postData = null;
        /// Cookie
        Public $cookie = "";
        /// Referer
        Public $referer = "";
        /// 连接超时
        public $timeOut = 5;
        /// 读取缓存大小
        public $cacheSize = 2048;
        /// 是否阻塞模式
        public $blocked = TRUE;

        ///构造函数，根据参数数量选择对应方法
        function __construct()
        {
            $a = func_get_args();
            $i = func_num_args();
            if (method_exists($this, $f="__construct".$i))
            {
                call_user_func_array(array($this,$f),$a);
            }
        }

        /// 构造函数派生方法
        private function __construct2($Url,$Method)
        {
            $this->parse_base_url($Url,$Method);
        }
        private function __construct3($Url,$Method,$PostData)
        {
            $this->parse_base_url($url,$method);
            $this->$postData = !isset($PostData) ? $PostData : "";
        }

        /// URL地址解析
        private function parse_base_url($u,$m)
        {
            $col = parse_url($u);
            !isset($col["host"]) && $col["host"] = "";
            !isset($col["path"]) && $col["path"] = "/";
            !isset($col["query"]) && $col["query"] = "";
            !isset($col["port"]) && $col["port"] = "";
            $this->host = $col["host"];
            $this->port = !empty($col["port"]) ? $col["port"] : 80;
            $this->path = $col["path"];
            if(array_key_exists("query",$col))
            {
                $this->query = !isset($col["query"])?$col["query"]:"";
            }
            $this->method = ($m === "post") ? $m : "get";
        }

        /// HTTP头部解析
        private function parse_HTTP_Headers($headerStr)
        {
            $len = $pos = 0;
            $str = $headerStr;
            $reslt = array();
            $split = strpos($str, "\r\n") > 0 ? "\r\n" : "\n";
            $i = 1;
            while($pos < strlen($str))
            {

                $key = $val = "";
                $len = stripos($str, " ", $pos) - $pos;
                $key = str_replace(":", "", substr($str, $pos, $len));
                $pos += $len + 1;

                $len = stripos($str, $split, $pos) - $pos;
                $val = str_replace($split, "", substr($str, $pos, $len));
                $pos += $len + 2;
                $reslt[$key] = $val;
                if(strlen($str) - $pos == 2)  break;
            }
            return $reslt;
        }

        /// 产生请求包
        protected function makeRequestStr()
        {
            $reqStr =($this->method === "get" ? "GET" : "POST")." ".$this->path.$this->query." HTTP/1.1 \r\n";
            $reqStr .="Host: ".$this->host.":".$this->port."\r\n";
            $reqStr .= "Accept: text/html,application/xhtml+xml,application/xml;image/webp,*/*\r\n";
            $reqStr .= "Accept-Language: zh-CN,zh\r\n";
            $reqStr .= "Accept-Encoding: gzip, deflate\r\n";
            $reqStr .=(($this->method === "post") ? "Content-Type: application/x-www-form-urlencoded\r\n" : "");
            $reqStr .="User-Agent: ".$_SERVER["HTTP_USER_AGENT"]."\r\n";
            $reqStr .=(($this->referer === "") ? "" : "Referer: ".$this->referer."\r\n");
            $reqStr .=(($this->cookie != "") ? "Cookie: ".$this->cookie."\r\n" : "");
            $reqStr .=(($this->method === "post") ? "Content-Length: ".strlen($this->postData)."\r\n" : "");
            $reqStr .="Connection: Close\r\n\r\n";
            $reqStr .=($this->method === "post" && !isset($this->postData)) ? $this->postData."\r\n\r\n" : "";
            return $reqStr;
        }

        /// gzip解压缩
        public function gzipDecode ($data) {
            $flags = ord(substr($data, 3, 1));
            $headerlen = 10;
            $extralen = 0;
            $filenamelen = 0;
            if ($flags & 4) {
                $extralen = unpack('v' ,substr($data, 10, 2));
                $extralen = $extralen[1];
                $headerlen += 2 + $extralen;
            }
            if ($flags & 8) // Filename
                $headerlen = strpos($data, chr(0), $headerlen) + 1;
            if ($flags & 16) // Comment
                $headerlen = strpos($data, chr(0), $headerlen) + 1;
            if ($flags & 2) // CRC at end of file
                $headerlen += 2;
            $unpacked = @gzinflate(substr($data, $headerlen));
            if ($unpacked === FALSE)
                $unpacked = $data;
            return $unpacked;
        }

        /// chunked解码
        public function chunkedDecode($data)
        {
            $pos = 0;
            $temp = '';
            while($pos < strlen($data))
            {
                $len = strpos($data, "/r/n", $pos) - $pos;
                $str = substr($data, $pos, $len);
                $pos += $len + 2;
                $arr = explode(';', $str, 2);
                $len = hexdec($arr[0]);
                $temp .= substr($data, $pos, $len);
                $pos += $len + 2;
            }
            return $temp;
        }

        /// 开始连接目标主机发送请求
        public function action()
        {
            $RESULT = array();
            /// 连接目标主机
            $sock = fsockopen($this->host, $this->port, $errno, $errstr, $this->timeOut);
            if(!$sock)
            {
                /// 连接失败
                $RESULT["error"] = $errstr;
            }
            else
            {
                $header = $content = "";
                /// 设置阻塞模式
                stream_set_blocking($sock, $this->blocked);
                /// 设置超时
                stream_set_timeout($sock, $this->timeOut);
                /// 发送请求
                fwrite($sock, $this->makeRequestStr());
                /// 获取连接状态
                $status = stream_get_meta_data($sock);
                /// 判断是否超时
                if(!$status["timed_out"])
                {
                    /// 循环读取HTTP头部
                    while (!feof($sock))
                    {
                        $header .= $h = fgets($sock);
                        if($h && ($h == "\r\n" ||  $h == "\n")) break;
                    }
                    /// 解析头部
                    $RESULT = $this->parse_HTTP_Headers($header);
                    /// 循环读取正文
                    while(!feof($sock))
                    {
                        $data = fread($sock, $this->cacheSize);
                        $content .= $data;
                    }
                    /// 整合返回结果
                    if(array_key_exists("Content-Encoding", $RESULT))
                    {
                        /// gzip解压缩
                        if(strpos($RESULT["Content-Encoding"], "gzip") > -1)
                        {
                            $RESULT["Document_Body"] = $this->gzipDecode($content);
                        }
                        /// chunked解码
                        if(strpos($RESULT["Content-Encoding"], "chunked") > -1)
                        {
                            $RESULT["Document_Body"] = $this->chunkedDecode($content);
                        }
                    }
                    else
                    {
                        /// 未进行任何压缩编码直接返回结果
                        $RESULT["Document_Body"] = $content;
                    }
                    $RESULT["error"] = "";
                }
                else
                {
                    /// 发送数据超时
                    $RESULT["error"] = "目标主机发送数据超时!";
                }
            }
            /// 关闭连接
            fclose($sock);
            $sock = null;
            /// 返回结果
            return $RESULT;
        }
    }
?>